home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
TPUG - Toronto PET Users Group
/
TPUG Users Group CD
/
TPUG Users Group CD.iso
/
AMIGA
/
AMICUS
/
AMICUS05.ADF
/
demos
/
sweep.c
< prev
Wrap
C/C++ Source or Header
|
1986-01-15
|
9KB
|
281 lines
/* sweep.c AmigaLink 1/25/86 */
/*****************************************************************************
*
* Double-Buffered Sound Synthesis Example
*
* Sam Dicker
* 6 December 1985
* (created: 8 October 1985)
*
* This program the demonstrates double-buffered writing to an audio channel
* using the the hardware control commands. This technique can be used to
* synthesize sound in 'real-time'. This program uses the mouse as a simple
* input device and to keep the example simple, directly reads the mouse
* register.
*
* Real-time synthesis code should always be written in the fastest assembly
* language possible (unlike this example) and should try to pre-compute as
* much data as possible. In this example, a sine wave lookup table is
* pre-computed. Then, while the sound is being played, the table is scanned
* at a rate dependent on a variable (frequency) and the scanned values are
* copied into temporary buffers. This frequency variable is modified by
* mouse movement, effectively making the mouse a pitch control. In a 'real'
* program, since pitch is the only parameter being controlled, it would be
* much more efficient to use modify the 'period' and play one fixed sine wave< * waveform buffer (or one waveform for each octave).
*
* Two temporary buffers are used. One must be computed and sent to the audio
* device before the other one has finished playing; otherwise, the audio
* device turns off the sound making a pop. This program runs in software
* interrupts to insure that it gets adequate processor time to avoid this
* problem.
*
*****************************************************************************/
#include "exec/types.h"
#include "exec/memory.h"
#include "exec/interrupts.h"
#include "exec/errors.h"
#include "hardware/custom.h"
#include "libraries/dos.h"
#include "devices/audio.h"
#define BUFFERSIZE 250
#define SINETABLEPOWER2 10
#define SINETABLESIZE (1 << SINETABLEPOWER2)
#define SINETABLESTEP (2 * 3.141593 / SINETABLESIZE)
/* mouse register addresses */
#define XMOUSEREG (*((BYTE *)&joy0dat + 1))
#define YMOUSEREG (-(*(BYTE *)&joy0dat))
extern struct MsgPort *CreatePort();
extern struct Library *OpenLibrary();
extern struct Task *FindTask();
extern UWORD joy0dat;
/* channel allocation map */
UBYTE allocationMap[] = { 1, 8, 2, 4 };
struct Library *MathBase = 0; /* used by cleanUp to determine
* what needs to be 'cleaned up' */
struct MsgPort *allocPort = 0;
struct IOAudio *allocIOB = 0;
struct Device *device = 0;
struct Interrupt *interrupt = 0;
struct MsgPort *soundPort = 0;
BYTE *buffer[2] = { 0 };
struct IOAudio *soundIOB[2] = { 0 };
int newBuffer();
UBYTE sineTable[SINETABLESIZE];
ULONG angle = 0;
ULONG frequency = 0x2000000;
BYTE lastYMouse;
main()
{
int i;
FLOAT sine, cosine;
/* open the math library */
if ((MathBase = OpenLibrary("mathffp.library", 0)) == 0)
cleanUp("Cannot open math library");
/* generate the sine lookup table. The table could have been computed by
* calling the 'sin' function for each point, but this method is a little
* faster where great accuracy is not required */
for (i = 0, cosine = (sine = 0.0) + 1.0; i < SINETABLESIZE;
++i, sine += SINETABLESTEP * (cosine -= SINETABLESTEP * sine)) {
/* generate table values between -128 and 127 */
sineTable[i] = 127 * sine + 0.5;
}
/* read the starting mouse count */
lastYMouse = YMOUSEREG;
/* initialize I/O block to allocate a channel when the audio device is
* OpenDevice'd */
if ((allocPort = CreatePort("sound example", 0)) == 0)
cleanUp("Cannot create reply port");
if ((allocIOB = (struct IOAudio *)AllocMem(sizeof(struct IOAudio),
MEMF_PUBLIC | MEMF_CLEAR)) == 0)
cleanUp("Out of memory");
/* allocation precedence */
allocIOB->ioa_Request.io_Message.mn_Node.ln_Pri = -40;
allocIOB->ioa_Request.io_Message.mn_ReplyPort = allocPort;
/* allocate from any channel */
allocIOB->ioa_Data = allocationMap;
allocIOB->ioa_Length = sizeof(allocationMap);
/* open the audio device with channel allocation and check for errors */
switch (OpenDevice(AUDIONAME, 0, allocIOB, 0)) {
case IOERR_OPENFAIL:
cleanUp("Cannot open audio device");
case ADIOERR_ALLOCFAILED:
cleanUp("Cannot allocate audio channel");
}
device = allocIOB->ioa_Request.io_Device;
/* initialize the software interrupt structure */
if ((interrupt = (struct Interrupt *)AllocMem(sizeof(struct Interrupt),
MEMF_CLEAR | MEMF_PUBLIC)) == 0)
cleanUp("Out of memory");
interrupt->is_Code = (VOID (*)())newBuffer;
/* initialize the reply port for CMD_WRITE's to generate software
interrupts */
if ((soundPort = (struct MsgPort *)AllocMem(sizeof(struct MsgPort),
MEMF_CLEAR | MEMF_PUBLIC)) == 0)
cleanUp("Out of memory");
soundPort->mp_Flags = PA_SOFTINT;
soundPort->mp_SigTask = (struct Task *)interrupt;
soundPort->mp_Node.ln_Type = NT_MSGPORT;
NewList(&soundPort->mp_MsgList);
/* initialize both I/O blocks for the CMD_WRITES */
for (i = 0; i < 2; ++i) {
/* allocate waveform memory from chip addressable ram. AllocMem
* always allocates memory on a word boundary which is necessary
* for audio waveform data */
if ((buffer[i] = (BYTE *)AllocMem(BUFFERSIZE, MEMF_CHIP))
== 0)
cleanUp("Out of memory");
if ((soundIOB[i] = (struct IOAudio *)AllocMem(sizeof(struct IOAudio),
MEMF_PUBLIC | MEMF_CLEAR)) == 0)
cleanUp("Out of memory");
soundIOB[i]->ioa_Request.io_Message.mn_ReplyPort = soundPort;
soundIOB[i]->ioa_Request.io_Device = device;
soundIOB[i]->ioa_Request.io_Unit = allocIOB->ioa_Request.io_Unit;
soundIOB[i]->ioa_Request.io_Command = CMD_WRITE;
/* load the volume and period registers */
soundIOB[i]->ioa_Request.io_Flags = ADIOF_PERVOL;
soundIOB[i]->ioa_AllocKey = allocIOB->ioa_AllocKey;
soundIOB[i]->ioa_Data = buffer[i];
soundIOB[i]->ioa_Length = BUFFERSIZE;
/* some arbitrary period and volume */
soundIOB[i]->ioa_Period = 200;
soundIOB[i]->ioa_Volume = 64;
/* play one cycle of each buffer, then reply */
soundIOB[i]->ioa_Cycles = 1;
/* this really "primes the pump" by causing the reply port
* to generate a software interrupt and write the first buffers */
ReplyMsg(soundIOB[i]);
}
/* wait for CTRL-C to stop the program */
puts("Press CTRL-C to stop");
Wait(SIGBREAKF_CTRL_C);
/* free up resources and exit */
cleanUp("");
}
/* print an error message and free allocated resources */
cleanUp(message)
TEXT *message;
{
int i;
puts(message);
if (device != 0)
/* CloseDevice'ing with 'allocIOB' preforms an ADCMD_FREE on any
* channel allocated with 'allocIOB's ioa_AllocKey. ADCMD_FREE
* performs a CMD_RESET, which performs a CMD_FLUSH, which AbortIO's
* any CMD_WRITES to those channels */
CloseDevice(allocIOB);
for (i = 0; i < 2; ++i) {
if (soundIOB[i])
FreeMem(soundIOB[i], sizeof(struct IOAudio));
if (buffer[i])
FreeMem(buffer[i], BUFFERSIZE);
}
if (soundPort)
FreeMem(soundPort, sizeof(struct MsgPort));
if (interrupt)
FreeMem(interrupt, sizeof(struct Interrupt));
if (allocIOB)
FreeMem(allocIOB, sizeof(struct IOAudio));
if (allocPort)
DeletePort(allocPort, sizeof(struct MsgPort));
if (MathBase)
CloseLibrary(MathBase);
exit();
}
/* software interrupt server code */
newBuffer()
{
int i;
struct IOAudio *ioa;
BYTE *buffer;
BYTE mouseChange, curYMouse;
ULONG newFreq;
/* get I/O block from reply port */
ioa = (struct IOAudio *)GetMsg(soundPort);
/* check if there really was an I/O blocks on the port and if there
* are no errors. An error would indicate either the channel was
* aborted from being stolen (IOERR_ABORTED), it stolen before the
* write was performed and had the wrong allocation key
* (ADIOF_NOALLOCATION), or it was aborted by being CloseDevice'd
* In any case if there is an error do not send the next write. The
* program will just wait around silently */
if (ioa && ioa->ioa_Request.io_Error == 0) {
/* determine how far the mouse has moved */
curYMouse = YMOUSEREG;
mouseChange = curYMouse - lastYMouse;
lastYMouse = curYMouse;
/* modify the frequency proportionally */
newFreq = frequency + mouseChange * (frequency >> 6);
/* limit the frequency range */
if (newFreq > 0x800000 && newFreq < 0x40000000)
frequency = newFreq;
/* scan the table and copy each new sample into the audio waveform
* buffer */
for (i = 0, buffer = ioa->ioa_Data; i < BUFFERSIZE; ++i)
*buffer++ = sineTable[(angle += frequency) >>
(32 - SINETABLEPOWER2)];
/* send the write I/O block */
BeginIO(ioa);
}
}